#ifndef MATRIX3D_H
#define MATRIX3D_H

#include <QDebug>
#include <QObject>
#include "QtGui/QOpenGLFunctions"


class Matrix3D : public QObject
{
    Q_OBJECT
public:
    Matrix3D() {}
    virtual ~Matrix3D() {
    }
    int count = 4;
    static GLfloat rc(GLfloat *array, int r, int c) { //rc: row and column
        return array[r * 4 + c];
    }
    static void appendValuesFloat(GLfloat *m,
                                   float v00, float v01, float v02, float v03,
                                   float v04, float v05, float v06, float v07,
                                   float v08, float v09, float v10, float v11,
                                   float v12, float v13, float v14, float v15) {
        m[ 0] = v00; m[ 1] = v01; m[ 2] = v02; m[ 3] = v03;
        m[ 4] = v04; m[ 5] = v05; m[ 6] = v06; m[ 7] = v07;
        m[ 8] = v08; m[ 9] = v09; m[10] = v10; m[11] = v11;
        m[12] = v12; m[13] = v13; m[14] = v14; m[15] = v15;

    }
    static void appendValuesFromMatrix(GLfloat *matrixToCopy, GLfloat *targetMatrix) {
        for (int i = 0; i < 16; i++) {
            targetMatrix[i] = matrixToCopy[i];
        }
    }
    static void appendValuesDouble(GLfloat *m,
                                   double v00, double v01, double v02, double v03,
                                   double v04, double v05, double v06, double v07,
                                   double v08, double v09, double v10, double v11,
                                   double v12, double v13, double v14, double v15) {
        m[ 0] = v00; m[ 1] = v01; m[ 2] = v02; m[ 3] = v03;
        m[ 4] = v04; m[ 5] = v05; m[ 6] = v06; m[ 7] = v07;
        m[ 8] = v08; m[ 9] = v09; m[10] = v10; m[11] = v11;
        m[12] = v12; m[13] = v13; m[14] = v14; m[15] = v15;

    }
    /** 打印一个矩阵 */
    static void printSingleMatrix(QString name, GLfloat *matrix) {
        qDebug() << name;
        qDebug() << matrix[0] << " " << matrix [1] << " " << matrix[2] << " " << matrix [3];
        qDebug() << matrix[4] << " " << matrix [5] << " " << matrix[6] << " " << matrix [7];
        qDebug() << matrix[8] << " " << matrix [9] << " " << matrix[10] << " " << matrix [11];
        qDebug() << matrix[12] << " " << matrix [13] << " " << matrix[14] << " " << matrix [15];
    }
    static GLfloat *newInstance() {
        GLfloat *array = new GLfloat[16];
        attachNewInstance(array);
        return array;
    }
    static void attachNewInstance(GLfloat *array) {
        array[0]  = 1; array[1]  = 0; array[2]  = 0; array[3]  = 0;
        array[4]  = 0; array[5]  = 1; array[6]  = 0; array[7]  = 0;
        array[8]  = 0; array[9]  = 0; array[10] = 1; array[11] = 0;
        array[12] = 0; array[13] = 0; array[14] = 0; array[15] = 1;
    }
    static void instance(double *array) {
        array[0]  = 1; array[1]  = 0; array[2]  = 0; array[3]  = 0;
        array[4]  = 0; array[5]  = 1; array[6]  = 0; array[7]  = 0;
        array[8]  = 0; array[9]  = 0; array[10] = 1; array[11] = 0;
        array[12] = 0; array[13] = 0; array[14] = 0; array[15] = 1;
    }
    static void scaleNew(double sx, double sy, double sz, GLfloat *scaleMatrix) {
        scaleMatrix[0]  = sx; scaleMatrix[1]  =  0; scaleMatrix[2]  =  0; scaleMatrix[3] = 0;
        scaleMatrix[4]  =  0; scaleMatrix[5]  = sy; scaleMatrix[6]  =  0; scaleMatrix[7] = 0;
        scaleMatrix[8]  =  0; scaleMatrix[9]  =  0; scaleMatrix[10] = sz; scaleMatrix[11] = 0;
        scaleMatrix[12] =  0; scaleMatrix[13] =  0; scaleMatrix[14] =  0; scaleMatrix[15] = 1;
    }
    static GLfloat *scale(double sx, double sy, double sz) {
        GLfloat *scaleMatrix = new GLfloat[16];
        scaleNew(sx, sy, sz, scaleMatrix);
        return scaleMatrix;
    }
    static void moveNew(double mx, double my, double mz, GLfloat *moveMatrix) {
        moveMatrix[0]  =  1; moveMatrix[1]  =  0; moveMatrix[2]  =  0; moveMatrix[3] = 0;
        moveMatrix[4]  =  0; moveMatrix[5]  =  1; moveMatrix[6]  =  0; moveMatrix[7] = 0;
        moveMatrix[8]  =  0; moveMatrix[9]  =  0; moveMatrix[10] =  1; moveMatrix[11] = 0;
        moveMatrix[12] = mx; moveMatrix[13] = my; moveMatrix[14] = mz; moveMatrix[15] = 1;

    }
    static GLfloat *move(double mx, double my, double mz) {
        GLfloat *moveMatrix = new GLfloat[16];
        moveNew(mx, my, mz, moveMatrix);
        return moveMatrix;
    }

    /** 以欧拉角的形式，返回一个物体的旋转矩阵 */
    //static GLfloat *rotationMatrixInRadian(double angleXInRadian, double angleYInRadian, double angleZInRadian);

    /** 计算反矩阵 */
    static GLfloat *inverseMatrix(GLfloat *m);

    /** 新版计算反矩阵 */
    static void inverseMatrixNew(GLfloat *m, GLfloat *array);

    /** 拷贝矩阵 */
    static GLfloat *copySingleMatrix(GLfloat *matrix1) {
        GLfloat *matrix = new GLfloat[16];
        for (int i = 0; i < 16; i++) {
            matrix[i] = matrix1[i];
        }
        return matrix;
    }

    static void multiplyTwoMatrices(GLfloat *m1, GLfloat *m2) {
        GLfloat b00 = m2[0 * 4 + 0];
                GLfloat b01 = m2[0 * 4 + 1];
                GLfloat b02 = m2[0 * 4 + 2];
                GLfloat b03 = m2[0 * 4 + 3];
                GLfloat b10 = m2[1 * 4 + 0];
                GLfloat b11 = m2[1 * 4 + 1];
                GLfloat b12 = m2[1 * 4 + 2];
                GLfloat b13 = m2[1 * 4 + 3];
                GLfloat b20 = m2[2 * 4 + 0];
                GLfloat b21 = m2[2 * 4 + 1];
                GLfloat b22 = m2[2 * 4 + 2];
                GLfloat b23 = m2[2 * 4 + 3];
                GLfloat b30 = m2[3 * 4 + 0];
                GLfloat b31 = m2[3 * 4 + 1];
                GLfloat b32 = m2[3 * 4 + 2];
                GLfloat b33 = m2[3 * 4 + 3];
                GLfloat a00 = m1[0 * 4 + 0];
                GLfloat a01 = m1[0 * 4 + 1];
                GLfloat a02 = m1[0 * 4 + 2];
                GLfloat a03 = m1[0 * 4 + 3];
                GLfloat a10 = m1[1 * 4 + 0];
                GLfloat a11 = m1[1 * 4 + 1];
                GLfloat a12 = m1[1 * 4 + 2];
                GLfloat a13 = m1[1 * 4 + 3];
                GLfloat a20 = m1[2 * 4 + 0];
                GLfloat a21 = m1[2 * 4 + 1];
                GLfloat a22 = m1[2 * 4 + 2];
                GLfloat a23 = m1[2 * 4 + 3];
                GLfloat a30 = m1[3 * 4 + 0];
                GLfloat a31 = m1[3 * 4 + 1];
                GLfloat a32 = m1[3 * 4 + 2];
                GLfloat a33 = m1[3 * 4 + 3];
                m2[ 0] = b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30;
                m2[ 1] = b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31;
                m2[ 2] = b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32;
                m2[ 3] = b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33;
                m2[ 4] = b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30;
                m2[ 5] = b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31;
                m2[ 6] = b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32;
                m2[ 7] = b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33;
                m2[ 8] = b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30;
                m2[ 9] = b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31;
                m2[10] = b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32;
                m2[11] = b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33;
                m2[12] = b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30;
                m2[13] = b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31;
                m2[14] = b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32;
                m2[15] = b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33;
    }

    static void multiplyTwoMatricesNew(GLfloat *m1, GLfloat *m2, GLfloat *array) {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < 4; c++) {
                int mIndex = r * 4 + c;
                GLfloat total = 0;
                for (int t = 0; t < 4; t++) {
                    // @ts-ignore
                    total += rc(m1, r, t) * rc(m2, t, c);
                }
                array[mIndex] = total;
            }
        }
    }

    /** 两个矩阵相乘 */
    static GLfloat *multiplyTwoVertices(GLfloat *m1, GLfloat *m2) {
        GLfloat *matrix = new GLfloat[16];
        multiplyTwoMatricesNew(m1, m2, matrix);
        return matrix;
    }

    /** 多个矩阵相乘 */
    static GLfloat *multiplyVertices(QList<GLfloat*> *matrices) {
        GLfloat *matrix;
        for (int i = 0; i < matrices->length(); i++) {
            GLfloat *sMatrix = matrices->at(i);
            if (i == 0) matrix = copySingleMatrix(sMatrix);
            else matrix = multiplyTwoVertices(matrix, sMatrix);
        }
        return matrix;
    }

    static void transposeMatrixNew(GLfloat *matrix, GLfloat *targetMatrix) {
        for (int r = 0; r < 4; r++) {
            for (int c = 0; c < 4; c++) {
                targetMatrix[r * 4 + c] = matrix[c * 4 + r];
            }
        }
    }

    /** 将矩阵进行转置 */
    static GLfloat *transposeMatrix(GLfloat *matrix) {
        GLfloat *returnMatrix = new GLfloat[16];
        transposeMatrixNew(matrix, returnMatrix);
        return returnMatrix;
    }

    /** 根据矩阵将空间中的某一个值转换成OpenGL显示范围内的值 */
    static GLfloat *convertToTheValueWithinOpenGLRange(GLfloat *matrix, GLdouble x, GLdouble y, GLdouble z) {
        GLfloat *returnValue = new GLfloat[3];
        GLfloat newX = (matrix[0] * x + matrix[4] * y + matrix[8] * z + matrix[12]);
        GLfloat newY = (matrix[1] * x + matrix[5] * y + matrix[9] * z + matrix[13]);
        GLfloat newZ = (matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14]);
        GLfloat valueToDivide = matrix[3] * x + matrix[7] + matrix[11] * z + matrix[15];

        returnValue[0] = newX / valueToDivide;
        returnValue[1] = newY / valueToDivide;
        returnValue[2] = newZ / valueToDivide;

        qDebug() << "X: " << returnValue[0] << "\nY: " << returnValue[1] << "\nZ: " << returnValue[2];
        return returnValue;
    }
};

#endif // MATRIX3D_H
